﻿using AutoMapper;
using BJoin.Application.BObject;
using BJoin.Application.Dto;
using BJoin.Application.Entity;
using BJoin.Application.IRepository;
using BJoin.Common;
using BJoin.DapperExtension.DatabaseFactory;
using BJoin.DapperExtension.Linq;
using BJoin.IDGenEx;
using DapperExtensions;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace BJoin.Application.IManager
{
    public abstract class PocoManager<TDto, TEntity> : ShardingDataProvider<IDatabase, long?>,
        IPocoManager<TDto, TEntity>
        where TEntity : BaseEntity
        where TDto : ResDto
    {
        /// <summary>
        /// DI Container Provider
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        protected IBaseRepository<TEntity> Repository { get; }
        /// <summary>
        /// Automapper's Mapper
        /// </summary>
        protected IMapper Mapper { get; }

        protected PocoManager(ILogger<PocoManager<TDto, TEntity>> logger, IServiceProvider provider,
            IBaseRepository<TEntity> repository, IMapper mapper, IDbFactory<IDatabase, long?> factory) : base(logger, factory)
        {
            ServiceProvider = provider;
            Repository = repository;
            Mapper = mapper;
        }

        #region Common Async
        public virtual async Task<long> CountAsync<TFilter>(TFilter filter, Func<TFilter, object> filterTranslator = null) where TFilter : ReqFilter
        {
            filterTranslator?.Invoke(filter);
            return await ReadAsync(async database => await Repository.CountAsync(database, filter));
        }

        public virtual async Task<long> CountAsync(Expression<Func<TEntity, bool>> expression)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            return await ReadAsync(async database => await Repository.CountAsync(database, predicate));
        }
        public virtual async Task<IEnumerable<TDto>> FilterAsync<TFilter>(TFilter filter, int? firstResult = null, int? maxResults = null, string[] orderBy = null,
            Func<TFilter, object> filterTranslator = null, Action<List<TDto>> dataTranslator = null) where TFilter : ReqFilter
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entities = await ReadAsync(async database => await Repository.GetListAsync(database, firstResult, maxResults, predicate ?? filter, orderBy));
            var dtos = Mapper.Map<IEnumerable<TDto>>(entities);
            if (dtos.IsAny())
                dataTranslator?.Invoke(dtos.ToList());
            return dtos;
        }

        public virtual async Task<IEnumerable<TDto>> FilterAsync(Expression<Func<TEntity, bool>> expression, int? firstResult = null, int? maxResults = null, string[] orderBy = null, Action<List<TDto>> dataTranslator = null)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            var entities = await ReadAsync(async database => await Repository.GetListAsync(database, firstResult, maxResults, predicate, orderBy));
            var dtos = Mapper.Map<IEnumerable<TDto>>(entities);
            if (dtos.IsAny())
                dataTranslator?.Invoke(dtos.ToList());
            return dtos;
        }

        public async Task<ResPagingDto<TDto>> PagingFilterAsync<TFilter>(TFilter filter, int pageIndex = 0, int pageSize = 20, string[] orderBy = null,
            Func<TFilter, object> filterTranslator = null, Action<List<TDto>> dataTranslator = null) where TFilter : ReqFilter
        {
            var predicate = filterTranslator?.Invoke(filter);
            var pagingEntities = await ReadAsync(async database => await Repository.GetListPagingAsync(database, pageIndex, pageSize, predicate ?? filter, orderBy));
            var pagingDto = Mapper.Map<ResPagingDto<TDto>>(pagingEntities);
            if (null != pagingDto && pagingDto.Items.IsAny())
                dataTranslator?.Invoke(pagingDto.Items.ToList());
            return pagingDto;
        }

        public virtual async Task<ResPagingDto<TDto>> PagingFilterAsync(Expression<Func<TEntity, bool>> expression, int pageIndex = 0, int pageSize = 20, string[] orderBy = null, Action<List<TDto>> dataTranslator = null)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            var pagingEntities = await ReadAsync(async database => await Repository.GetListPagingAsync(database, pageIndex, pageSize, predicate, orderBy));
            var pagingDto = Mapper.Map<ResPagingDto<TDto>>(pagingEntities);
            if (null != pagingDto && pagingDto.Items.IsAny())
                dataTranslator?.Invoke(pagingDto.Items.ToList());
            return pagingDto;
        }
        public virtual async Task<bool> AddAsync<TAddDto>(TAddDto addDto) where TAddDto : ReqAddDto
        {
            var entity = await AddDto2EntityAsync(addDto);
            return await WriteAsync(async database => await Repository.InsertAsync(database, entity));
        }
        public virtual async Task<bool> AddManyAsync<TAddDto>(IEnumerable<TAddDto> addDtos) where TAddDto : ReqAddDto
        {
            var entities = await AddDto2EntityAsync(addDtos);
            return await WriteAsync(async database => await Repository.InsertManyAsync(database, entities));

        }

        public virtual async Task<bool> UpdateSetAsync<TFilter, TUpdateDto>(TUpdateDto updateDto, TFilter filter, Func<TFilter, object> filterTranslator = null) where TUpdateDto : ReqUpdateDto
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateSetAsync(database, entity, predicate??filter));
        }

        public virtual async Task<bool> UpdateAsync<TFilter, TUpdateDto>(TUpdateDto updateDto, TFilter filter, Func<TFilter, object> filterTranslator = null)
            where TFilter : ReqFilter
            where TUpdateDto : ReqUpdateDto
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateAsync(database, entity, predicate??filter));
        }

        public virtual async Task<bool> DeleteAsync<TFilter>(TFilter filter, Func<TFilter, object> filterTranslator = null) where TFilter : ReqFilter
        {
            var predicate = filterTranslator?.Invoke(filter);
            return await WriteAsync(async database => await Repository.DeleteAsync(database, predicate ?? filter));
        }
        public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> expression)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            return await WriteAsync(async database => await Repository.DeleteAsync(database, predicate));
        }
        protected async virtual Task<TEntity> AddDto2EntityAsync<TAddDto>(TAddDto addDto) where TAddDto : ReqAddDto
        {
            await BizValidaterAsync(addDto);
            var entity = Mapper.Map<TEntity>(addDto);
            return entity;
        }
        protected async virtual Task<IEnumerable<TEntity>> AddDto2EntityAsync<TAddDto>(IEnumerable<TAddDto> addDtos) where TAddDto : ReqAddDto
        {
            await BizValidaterAsync(addDtos);
            var entities = Mapper.Map<IEnumerable<TEntity>>(addDtos);
            return entities;
        }
        protected virtual async Task BizValidaterAsync<TAddDto>(TAddDto addDto)
            where TAddDto : ReqAddDto => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual async Task BizValidaterAsync<TAddDto>(IEnumerable<TAddDto> addDtos)
            where TAddDto : ReqAddDto => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual void NotImplemented(string methodName)
        {
            throw new NotImplementedException($"{methodName} Missing implementation");
        }
        #endregion
    }
    public abstract class PocoManager<TDto, TEntity, TKey> : ShardingDataProvider<IDatabase, long?>,
        IPocoManager<TDto, TEntity, TKey>
        where TEntity : BaseEntity<TKey>
        where TDto : ResDto<TKey>
    {
        /// <summary>
        /// DI Container Provider
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        protected IBaseRepository<TEntity, TKey> Repository { get; }
        /// <summary>
        /// Automapper's Mapper
        /// </summary>
        protected IMapper Mapper { get; }

        protected PocoManager(ILogger<PocoManager<TDto,TEntity,TKey>> logger, IServiceProvider provider,
            IBaseRepository<TEntity, TKey> repository, IMapper mapper, IDbFactory<IDatabase, long?> factory):base(logger,factory)
        {
            ServiceProvider = provider;
            Repository = repository;
            Mapper = mapper;
        }

        #region Common Async
        public virtual async Task<long> CountAsync<TFilter>(TFilter filter, Func<TFilter,object> filterTranslator = null) where TFilter : ReqFilter
        {
            filterTranslator?.Invoke(filter);
            return await ReadAsync(async database => await Repository.CountAsync(database, filter));
        }

        public virtual async Task<long> CountAsync(Expression<Func<TEntity, bool>> expression)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            return await ReadAsync(async database => await Repository.CountAsync(database, predicate));
        }

        public virtual async Task<TDto> GetSingleAsync(TKey id, Action<TDto> dataTranslator = null)
        {
            TEntity entity = await ReadAsync(async database => await Repository.GetAsync(database, id));
            var dto = Mapper.Map<TDto>(entity);
            if (null != dto)
                dataTranslator?.Invoke(dto);
            return dto;
        }
        public virtual async Task<IEnumerable<TDto>> FilterAsync<TFilter>(TFilter filter, int? firstResult = null, int? maxResults = null, string[] orderBy = null,
            Func<TFilter,object> filterTranslator = null, Action<List<TDto>> dataTranslator = null) where TFilter : ReqFilter
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entities = await ReadAsync(async database => await Repository.GetListAsync(database, firstResult, maxResults, predicate??filter, orderBy));
            var dtos = Mapper.Map<IEnumerable<TDto>>(entities);
            if (dtos.IsAny())
                dataTranslator?.Invoke(dtos.ToList());
            return dtos;
        }

        public virtual async Task<IEnumerable<TDto>> FilterAsync(Expression<Func<TEntity, bool>> expression, int? firstResult = null, int? maxResults = null, string[] orderBy = null, Action<List<TDto>> dataTranslator = null)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            var entities = await ReadAsync(async database => await Repository.GetListAsync(database, firstResult, maxResults, predicate, orderBy));
            var dtos = Mapper.Map<IEnumerable<TDto>>(entities);
            if (dtos.IsAny())
                dataTranslator?.Invoke(dtos.ToList());
            return dtos;
        }

        public async Task<ResPagingDto<TDto>> PagingFilterAsync<TFilter>(TFilter filter, int pageIndex = 0, int pageSize = 20, string[] orderBy = null,
            Func<TFilter,object> filterTranslator = null, Action<List<TDto>> dataTranslator = null) where TFilter : ReqFilter
        {
            var predicate = filterTranslator?.Invoke(filter);
            var pagingEntities = await ReadAsync(async database => await Repository.GetListPagingAsync(database, pageIndex, pageSize, predicate?? filter, orderBy));
            var pagingDto = Mapper.Map<ResPagingDto<TDto>>(pagingEntities);
            if (null != pagingDto && pagingDto.Items.IsAny())
                dataTranslator?.Invoke(pagingDto.Items.ToList());
            return pagingDto;
        }

        public virtual async Task<ResPagingDto<TDto>> PagingFilterAsync(Expression<Func<TEntity, bool>> expression, int pageIndex = 0, int pageSize = 20, string[] orderBy = null, Action<List<TDto>> dataTranslator = null)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            var pagingEntities = await ReadAsync(async database => await Repository.GetListPagingAsync(database, pageIndex, pageSize, predicate, orderBy));
            var pagingDto = Mapper.Map<ResPagingDto<TDto>>(pagingEntities);
            if (null != pagingDto && pagingDto.Items.IsAny())
                dataTranslator?.Invoke(pagingDto.Items.ToList());
            return pagingDto;
        }
        public virtual async Task<TKey> AddAsync<TAddDto>(TAddDto addDto) where TAddDto : ReqAddDto
        {
            var entity = await AddDto2EntityAsync(addDto);
            return await WriteAsync(async database => await Repository.InsertAsync(database, entity));
        }
        public virtual async Task<IEnumerable<TKey>> AddManyAsync<TAddDto>(IEnumerable<TAddDto> addDtos) where TAddDto : ReqAddDto
        {
            var entities = await AddDto2EntityAsync(addDtos);
            return await WriteAsync(async database => await Repository.InsertManyAsync(database, entities));

        }

        public virtual async Task<bool> UpdateSetAsync<TUpdateDto>(TUpdateDto updateDto, TKey id) where TUpdateDto : ReqUpdateDto
        {
            await BizValidaterAsync(updateDto, id);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateSetAsync(database, entity, new { id }));
        }
        public virtual async Task<bool> UpdateSetAsync<TFilter, TUpdateDto>(TUpdateDto updateDto, TFilter filter, Func<TFilter, object> filterTranslator = null) where TUpdateDto : ReqUpdateDto
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateSetAsync(database, entity, predicate??filter));
        }
        public virtual async Task<bool> UpdateAsync<TUpdateDto>(TUpdateDto updateDto, TKey id) where TUpdateDto : ReqUpdateDto
        {
            await BizValidaterAsync(updateDto,id);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateAsync(database, entity, new { id }));
        }

        public virtual async Task<bool> UpdateAsync<TFilter, TUpdateDto>(TUpdateDto updateDto, TFilter filter, Func<TFilter, object> filterTranslator = null)
            where TFilter : ReqFilter
            where TUpdateDto : ReqUpdateDto
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateAsync(database, entity, predicate??filter));
        }
        public virtual async Task<bool> DeleteAsync(TKey[] id)
        {
            return await WriteAsync(async database => await Repository.DeleteAsync(database, id));
        }

        public virtual async Task<bool> DeleteAsync<TFilter>(TFilter filter, Func<TFilter, object> filterTranslator = null) where TFilter : ReqFilter
        {
            var predicate = filterTranslator?.Invoke(filter);
            return await WriteAsync(async database => await Repository.DeleteAsync(database, predicate??filter));
        }
        public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> expression)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            return await WriteAsync(async database => await Repository.DeleteAsync(database, predicate));
        }
        protected async virtual Task<TEntity> AddDto2EntityAsync<TAddDto>(TAddDto addDto) where TAddDto : ReqAddDto
        {
            await BizValidaterAsync(addDto);
            var entity = Mapper.Map<TEntity>(addDto);
            if (typeof(TKey) == typeof(long))
            {
                var id = new IDGenerator(0).CreateId();
                entity.Id = (TKey)Convert.ChangeType(id, typeof(TKey));
            }
            return entity;
        }
        protected async virtual Task<IEnumerable<TEntity>> AddDto2EntityAsync<TAddDto>(IEnumerable<TAddDto> addDtos) where TAddDto : ReqAddDto
        {
            await BizValidaterAsync(addDtos);
            var entities = Mapper.Map<IEnumerable<TEntity>>(addDtos);
            //assign Id for each item
            if (typeof(TKey) == typeof(long))
            {
                var ids = new IDGenerator(0).Take(addDtos.Count());
                foreach (var item in entities.Zip(ids, (a, b) => new { a, b }))
                {
                    item.a.Id = (TKey)Convert.ChangeType(item.b, typeof(TKey));
                }
            }
            return entities;
        }
        protected virtual async Task BizValidaterAsync<TAddDto>(TAddDto addDto)
            where TAddDto : ReqAddDto => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual async Task BizValidaterAsync<TAddDto>(IEnumerable<TAddDto> addDtos)
            where TAddDto : ReqAddDto => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual async Task BizValidaterAsync<TUpdateDto>(TUpdateDto updateDto, TKey id)
            where TUpdateDto : ReqUpdateDto => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual void NotImplemented(string methodName)
        {
            throw new NotImplementedException($"{methodName} Missing implementation");
        }
        #endregion
    }
    public abstract class PocoManager<TDto, TEntity, TKey, TExt> : ShardingDataProvider<IDatabase, long?>,
        IPocoManager<TDto, TEntity, TKey, TExt>
        where TEntity : BaseEntity<TKey, TExt>
        where TDto : ResDto<TKey, TExt>
        where TExt : BObjExt
    {
        /// <summary>
        /// DI Container Provider
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        protected IBaseRepository<TEntity, TKey, TExt> Repository { get; }
        /// <summary>
        /// Automapper's Mapper
        /// </summary>
        protected IMapper Mapper { get; }

        protected PocoManager(ILogger<PocoManager<TDto, TEntity, TKey, TExt>> logger, IServiceProvider provider,
            IBaseRepository<TEntity, TKey, TExt> repository, IMapper mapper, IDbFactory<IDatabase, long?> factory) : base(logger, factory)
        {
            ServiceProvider = provider;
            Repository = repository;
            Mapper = mapper;
        }
        #region Common Async
        public virtual async Task<long> CountAsync<TFilter>(TFilter filter, Func<TFilter,object> filterTranslator = null) where TFilter : ReqFilter<TKey, TExt>
        {
            filterTranslator?.Invoke(filter);
            return await ReadAsync(async database => await Repository.CountAsync(database, filter));
        }

        public virtual async Task<long> CountAsync(Expression<Func<TEntity, bool>> expression)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            return await ReadAsync(async database => await Repository.CountAsync(database, predicate));
        }

        public virtual async Task<TDto> GetSingleAsync(TKey id, Action<TDto> dataTranslator = null)
        {
            TEntity entity = await ReadAsync(async database => await Repository.GetAsync(database, id));
            var dto = Mapper.Map<TDto>(entity);
            if (null != dto)
                dataTranslator?.Invoke(dto);
            return dto;
        }
        public virtual async Task<IEnumerable<TDto>> FilterAsync<TFilter>(TFilter filter, int? firstResult = null, int? maxResults = null, string[] orderBy = null,
            Func<TFilter,object> filterTranslator = null, Action<List<TDto>> dataTranslator = null) where TFilter : ReqFilter<TKey, TExt>
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entities = await ReadAsync(async database => await Repository.GetListAsync(database,firstResult,maxResults, predicate??filter, orderBy));
            var dtos = Mapper.Map<IEnumerable<TDto>>(entities);
            if (dtos.IsAny())
                dataTranslator?.Invoke(dtos.ToList());
            return dtos;
        }

        public virtual async Task<IEnumerable<TDto>> FilterAsync(Expression<Func<TEntity, bool>> expression, int? firstResult = null, int? maxResults = null, string[] orderBy = null, Action<List<TDto>> dataTranslator = null)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            var entities = await ReadAsync(async database => await Repository.GetListAsync(database, firstResult, maxResults, predicate, orderBy));
            var dtos = Mapper.Map<IEnumerable<TDto>>(entities);
            if (dtos.IsAny())
                dataTranslator?.Invoke(dtos.ToList());
            return dtos;
        }

        public async Task<ResPagingDto<TDto>> PagingFilterAsync<TFilter>(TFilter filter, int pageIndex = 0, int pageSize = 20, string[] orderBy = null,
            Func<TFilter,object> filterTranslator = null, Action<List<TDto>> dataTranslator = null) where TFilter : ReqFilter<TKey, TExt>
        {
            var predicate = filterTranslator?.Invoke(filter);
            var pagingEntities = await ReadAsync(async database => await Repository.GetListPagingAsync(database, pageIndex, pageSize, predicate??filter, orderBy));
            var pagingDto = Mapper.Map<ResPagingDto<TDto>>(pagingEntities);
            if (null != pagingDto && pagingDto.Items.IsAny())
                dataTranslator?.Invoke(pagingDto.Items.ToList());
            return pagingDto;
        }

        public virtual async Task<ResPagingDto<TDto>> PagingFilterAsync(Expression<Func<TEntity, bool>> expression, int pageIndex = 0, int pageSize = 20, string[] orderBy = null, Action<List<TDto>> dataTranslator = null)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            var pagingEntities = await ReadAsync(async database => await Repository.GetListPagingAsync(database, pageIndex, pageSize, predicate, orderBy));
            var pagingDto = Mapper.Map<ResPagingDto<TDto>>(pagingEntities);
            if (null != pagingDto && pagingDto.Items.IsAny())
                dataTranslator?.Invoke(pagingDto.Items.ToList());
            return pagingDto;
        }
        public virtual async Task<TKey> AddAsync<TAddDto>(TAddDto addDto) where TAddDto : ReqAddDto<TExt>
        {
            var entity = await AddDto2EntityAsync(addDto);
            return await WriteAsync(async database => await Repository.InsertAsync(database, entity));
        }
        public virtual async Task<IEnumerable<TKey>> AddManyAsync<TAddDto>(IEnumerable<TAddDto> addDtos) where TAddDto : ReqAddDto<TExt>
        {
            var entities = await AddDto2EntityAsync(addDtos);
            return await WriteAsync(async database => await Repository.InsertManyAsync(database, entities));

        }

        public virtual async Task<bool> UpdateSetAsync<TUpdateDto>(TUpdateDto updateDto, TKey id) where TUpdateDto : ReqUpdateDto<TExt>
        {
            await BizValidaterAsync(updateDto,id);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateSetAsync(database, entity, new { id }));
        }

        public virtual async Task<bool> UpdateSetAsync<TFilter, TUpdateDto>(TUpdateDto updateDto, TFilter filter, Func<TFilter, object> filterTranslator = null) where TUpdateDto : ReqUpdateDto<TExt>
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateSetAsync(database, entity, predicate??filter));
        }

        public virtual async Task<bool> UpdateAsync<TFilter, TUpdateDto>(TUpdateDto updateDto, TFilter filter, Func<TFilter, object> filterTranslator = null)
            where TFilter : ReqFilter<TKey, TExt>
            where TUpdateDto : ReqUpdateDto<TExt>
        {
            var predicate = filterTranslator?.Invoke(filter);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateAsync(database, entity, predicate??filter));
        }

        public virtual async Task<bool> UpdateAsync<TUpdateDto>(TUpdateDto updateDto, TKey id) where TUpdateDto : ReqUpdateDto<TExt>
        {
            await BizValidaterAsync(updateDto, id);
            var entity = Mapper.Map<TEntity>(updateDto);
            return await WriteAsync(async database => await Repository.UpdateAsync(database, entity, new { id }));
        }

        public virtual async Task<bool> DeleteAsync(TKey[] id)
        {
            return await WriteAsync(async database => await Repository.DeleteAsync(database, id));
        }

        public virtual async Task<bool> DeleteAsync<TFilter>(TFilter filter, Func<TFilter, object> filterTranslator = null) where TFilter : ReqFilter<TKey,TExt>
        {
            var predicate = filterTranslator?.Invoke(filter);
            return await WriteAsync(async database => await Repository.DeleteAsync(database, predicate ?? filter));
        }
        public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> expression)
        {
            var predicate = Expression2Predicate<TEntity>.FromExpression(expression);
            return await WriteAsync(async database => await Repository.DeleteAsync(database, predicate));
        }
        protected async virtual Task<TEntity> AddDto2EntityAsync<TAddDto>(TAddDto addDto) where TAddDto : ReqAddDto<TExt>
        {
            await BizValidaterAsync(addDto);
            var entity = Mapper.Map<TEntity>(addDto);
            if (typeof(TKey) == typeof(long))
            {
                var id = new IDGenerator(0).CreateId();
                entity.Id = (TKey)Convert.ChangeType(id, typeof(TKey));
            }
            return entity;
        }
        protected async virtual Task<IEnumerable<TEntity>> AddDto2EntityAsync<TAddDto>(IEnumerable<TAddDto> addDtos) where TAddDto : ReqAddDto<TExt>
        {
            await BizValidaterAsync(addDtos);
            var entities = Mapper.Map<IEnumerable<TEntity>>(addDtos);
            //assign Id for each item
            if (typeof(TKey) == typeof(long))
            {
                var ids = new IDGenerator(0).Take(addDtos.Count());
                foreach (var item in entities.Zip(ids, (a, b) => new { a, b }))
                {
                    item.a.Id = (TKey)Convert.ChangeType(item.b, typeof(TKey));
                }
            }
            return entities;
        }
        protected virtual async Task BizValidaterAsync<TAddDto>(TAddDto addDto)
            where TAddDto : ReqAddDto<TExt> => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual async Task BizValidaterAsync<TAddDto>(IEnumerable<TAddDto> addDtos)
            where TAddDto : ReqAddDto<TExt> => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual async Task BizValidaterAsync<TUpdateDto>(TUpdateDto updateDto, TKey id)
            where TUpdateDto : ReqUpdateDto<TExt> => await Task.Run(() => NotImplemented(MethodBase.GetCurrentMethod().Name));
        protected virtual void NotImplemented(string methodName)
        {
            throw new NotImplementedException($"{methodName} Missing implementation");
        }
        #endregion
    }
}
